﻿using Games.Basic;
using Games.Common;
using Games.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;

namespace Games.Sudoku
{
    /// <summary>
    /// 可选序列,封装一个整型的集合
    /// 提供再次打乱序列的方法
    /// </summary>
    public class Avails
    {
        private ISet<int> list = new HashSet<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        public List<int> List { get { return list.ToList(); } }
        public void Add(int i)
        {
            if(i > 9 || i < 1)
            {
                return;
            }
            list.Add(i);
        }
        public void Remove(int i)
        {
            if(i > 9 || i < 1)
            {
                return;
            }
            list.Remove(i);
        }
        public int Count { get { return list.Count; } }
    }
    /// <summary>
    /// 数独核心类
    /// 其实就是一个封装的矩阵
    /// 其中包括构造一个符合数独规则的矩阵,验证行列宫
    /// 提供为一个被置空的格子提供可选序列
    /// </summary>
    public class Matrix
    {
        private int[,] matrix;
        private int[,] full;
        private bool[,] knowns;
        private List<(int, int, int)> errors;

        /// <summary>
        /// 数独的空格数
        /// </summary>
        public int Empty { get; private set; }
        public bool IsFinished
        {
            get
            {
                if(Empty == 0 && errors.Count == 0)
                {
                    for(int i = 0; i < 9; i++)
                    {
                        for(int j = 0; j < 9; j++)
                        {
                            if(full[i, j] != matrix[i, j])
                            {
                                return false;
                            }
                        }
                    }
                    return true;
                }
                return false;
            }
        }
        public List<(int, int, int)> GetErrors()
        {
            return this.errors.ToList();
        }
        /// <summary>
        /// 从数组中加载游戏
        /// </summary>
        /// <param name="m"></param>
        public void LoadMatrix(MapData mapData)
        {
            this.full = mapData.FullDatas;
            int[,] m = mapData.Datas;
            this.matrix = new int[9, 9];
            this.knowns = new bool[9, 9];
            this.errors = new List<(int, int, int)>();
            this.Empty = 0;
            for(int r = 0; r < 9; r++)
            {
                for(int c = 0; c < 9; c++)
                {
                    this.matrix[r, c] = m[r, c];
                    if(m[r, c] == 0)
                        this.Empty++;
                    else
                        this.knowns[r, c] = true;
                }
            }
        }
        /// <summary>
        /// 获取指定行列的可选序列,该序列中的值不与所在行列宫中的任何一个值冲突
        /// </summary>
        /// <param name="r"></param>
        /// <param name="t"></param>
        /// <returns></returns>
        public List<int> GetOptional(int r, int c)
        {
            //初始化一个随机的可选序列,随机是为了防止多次生成一样的数独
            Avails ava = new Avails();
            //将本行出现过的值从序列中删除
            //将本列出现过的值从序列中删除
            for(var i = 0; i < 9; i++)
            {
                if(i != c)
                    ava.Remove(matrix[r, i]);
                if(i != r)
                    ava.Remove(matrix[i, c]);
            }
            //将本宫出现过的值从序列中删除
            for(int i = r / 3 * 3; i < r / 3 * 3 + 3; i++)
            {
                for(int j = c / 3 * 3; j < c / 3 * 3 + 3; j++)
                {
                    if(i != r && j != c)
                        ava.Remove(matrix[i, j]);
                }
            }
            return ava.List;
        }
        public int Get(int r, int c)
        {
            return this.matrix[r, c];
        }

        public bool IsFixed(int r, int c)
        {
            return this.knowns[r, c];
        }

        /// <summary>
        /// 用位运算 并 将指定值从所在的行列宫二进制状态中清空
        /// </summary>
        /// <param name="r">所在行</param>
        /// <param name="c">所在列</param>
        /// <param name="n">所在宫</param>
        public void ClearValue(int r, int c)
        {
            int v = matrix[r, c];
            if(v > 0)
            {
                matrix[r, c] = 0;
                RemoveErrors(r, c, v);
                Empty++;
            }
        }

        /// <summary>
        /// 用位运算 或 将指定值放进所在行列宫二进制状态中
        /// </summary>
        /// <param name="r">所在行</param>
        /// <param name="c">所在列</param>
        /// <param name="value">要放进的值</param>
        public void FillValue(int r, int c, int value)
        {
            if(this.knowns[r, c])
                return;
            if(value < 1 || value > 9)
                return;
            //清空该格子
            ClearValue(r, c);
            matrix[r, c] = value;
            Empty--;
            AddErrors(r, c);
        }

        /// <summary>
        /// 去除错误
        /// </summary>
        /// <param name="r">行</param>
        /// <param name="c">列</param>
        private void RemoveErrors(int r, int c, int value)
        {
            var any = this.errors.Count(x => x.Item1 == r && x.Item2 == c && x.Item3 == value);
            if(any == 0)
            {
                return;
            }
            this.errors.Remove((r, c, value));

            var verrors = this.errors.Where(x => x.Item3 == value).ToList();
            var vrs = verrors.Where(x => x.Item1 == r).ToList();
            if(vrs.Count == 1)
            {
                foreach(var item in vrs)
                {
                    this.errors.Remove(item);
                }
            }

            var vcs = verrors.Where(x => x.Item2 == c).ToList();
            if(vcs.Count == 1)
            {
                foreach(var item in vcs)
                {
                    this.errors.Remove(item);
                }
            }

            var n = r / 3 * 3 + c / 3;
            var vns = verrors.Where(x => x.Item1 / 3 * 3 + x.Item2 / 3 == n).ToList();
            if(vns.Count == 1)
            {
                foreach(var item in vns)
                {
                    this.errors.Remove(item);
                }
            }
        }

        /// <summary>
        /// 增加错误
        /// </summary>
        /// <param name="r">行</param>
        /// <param name="c">列</param>
        private void AddErrors(int r, int c)
        {
            var flag = false;
            var value = this.matrix[r, c];
            for(int i = 0; i < 9; i++)
            {
                if(r != i && this.matrix[i, c] == value)
                {
                    flag = true;
                    this.errors.Add((i, c, value));
                }
                if(c != i && this.matrix[r, i] == value)
                {
                    flag = true;
                    this.errors.Add((r, i, value));
                }
            }
            for(int i = r / 3 * 3; i < r / 3 * 3 + 3; i++)
            {
                for(int j = c / 3 * 3; j < c / 3 * 3 + 3; j++)
                {
                    if(i != r && j != c && this.matrix[i, j] == value)
                    {
                        flag = true;
                        this.errors.Add((i, j, value));
                    }
                }
            }
            if(flag)
            {
                this.errors.Add((r, c, value));
            }
        }
    }
    class GameInfo : BaseViewModel, IGameInfo
    {
        public string Name => "数独";
        public string Desc => "数独起源于18世纪的瑞士，号称“数字魔方”，是一种益智游戏，类似中国的九宫格游戏。" +
            "\r\n玩家需要根据9×9盘面上的已知数字，推理出所有剩余空格的数字，并满足每一行、每一列、每一个粗线宫（3*3）内的数字均含1-9，不重复。" +
            "\r\n数独盘面是个九宫，每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件，利用逻辑和推理，在其他的空格上填入1-9的数字。" +
            "\r\n使1-9每个数字在每一行、每一列和每一宫中都只出现一次，所以又称“九宫格”。";
        public string Version => "1.0";
        public string ID => "0B566923-7071-4066-B3C6-DA3B79447B6E";
        public string BuildDate => "20200713";
        public ContentPage NewPage() => PageHolder.Get(() => new SudokuPage(this));
        public int Level { get; set; }

        string levelString;
        public string LevelString
        {
            get { return levelString; }
            set { SetProperty(ref levelString, value); }
        }

        string no;
        public string No
        {
            get { return no; }
            set { SetProperty(ref no, value); }
        }
    }
    class SudokuLevel : BaseArgs
    {
        public static List<string> LEVELS = new List<string> { "入门", "简单", "普通", "困难", "极难" };
        private int max0;
        private int max1;
        private int max2;
        private int max3;
        private int max4;

        public int Level { get; set; }
        public int Index { get; set; }
        public int Max0 { get => max0; set => max0 = value; }
        public int Max1 { get => max1; set => max1 = value; }
        public int Max2 { get => max2; set => max2 = value; }
        public int Max3 { get => max3; set => max3 = value; }
        public int Max4 { get => max4; set => max4 = value; }
        public string No { get { return $"{Level + 1}{Index.ToString().PadLeft(3, '0')}"; } }
        public string LevelString { get { return LEVELS[Level]; } }

        public int GetMax()
        {
            switch(Level)
            {
                case 1:
                    return Max1;
                case 2:
                    return Max2;
                case 3:
                    return Max3;
                case 4:
                    return Max4;
                default:
                    return Max0;
            }
        }

        private bool Increase(int level, ref int maxx)
        {
            var max = Data.GetMax(level) - 1;
            if(Index < maxx - 1)
            {
                Index++;
                if(Index > max)
                {
                    Index = 0;
                }
                return true;
            }
            else if(maxx < max && maxx == Index)
            {
                maxx++;
                Index++;
                return true;
            }
            return false;
        }

        public bool Increase(int level)
        {
            switch(level)
            {
                case 1:
                    return Increase(level, ref max1);
                case 2:
                    return Increase(level, ref max2);
                case 3:
                    return Increase(level, ref max3);
                case 4:
                    return Increase(level, ref max4);
                default:
                    return Increase(level, ref max0);
            }
        }
    }
    class SudokuRecord : GameRecords
    {
        public string LevelString { get; set; }
        public string No { get; set; }

        public override Record Convert()
        {
            return new Record
            {
                Message = $"Lv.{LevelString} No.{No} - 用时{Utils.FormatTime((int)Duration)}",
                TimeString = CreateTime.ToString("yyyy-MM-dd HH:mm")
            };
        }
    }
    //class SudokuHistory : Entity
    //{
    //    public DateTime CreateTime { get; set; } = DateTime.Now;
    //    public int Level { get; set; }
    //    public int Index { get; set; }
    //    public string History { get; set; }
    //    public int Duration { get; set; }
    //}
}
